#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/klog.h>
#include <sys/mman.h>

#include <poll.h>

#include "dal_user.h"
#include "dal_kernel.h"

static int dal_dev_fd = -1;

dal_op_t g_dal_op =
{
	 init: dal_usr_init,
	 exit: dal_usr_exit,
    interrupt_register: dal_interrupt_register,
    interrupt_unregister: dal_interrupt_unregister,
    interrupt_set_en: dal_interrupt_set_en,
};

static intr_handler_t intr_handlers[DAL_MAX_INTR_NUM];


int dal_usr_init()
{
	unsigned char dal_name[32] = {0};
	snprintf((char*)dal_name, 16, "%s", DAL_DEV_NAME);
	if ((dal_dev_fd = sal_open((char *)dal_name, O_RDWR | O_SYNC | O_DSYNC | O_RSYNC)) < 0)
	{
		printf("open %s error!!", dal_name);
		return -1;
	}
	return 0;
}

int dal_usr_exit()
{
	sal_close(dal_dev_fd);
	return 0;
}


int dal_usrctrl_do_cmd(unsigned int cmd, unsigned long p_para)
{
    CHECK_FD(dal_dev_fd);
    CHECK_PTR(p_para);

    return ioctl(dal_dev_fd, cmd, p_para);
}

/* interrupt thread */
static void dal_intr_thread(void* param)
{
    unsigned long para = (unsigned long)param;
    dal_intr_para_t* p_para = (dal_intr_para_t*)&para;
    int ret = 0;
    int intr_idx = 0;
    int irq = 0;
    intr_handler_t* intr_handler = NULL;
    struct sal_pollfd poll_list;
    char dev_name[32];
    char nod_name[32];

    intr_idx = p_para->intr_idx;
    irq = p_para->irq;
    intr_handler = (intr_handler_t*)&(intr_handlers[intr_idx]);

    memset(dev_name, 0, 32);
    snprintf(dev_name, 32, "%s%d", DAL_INTR_DEV_NAME, intr_idx);
    snprintf(nod_name, 32, "mknod %s%d c %d 0", DAL_INTR_DEV_NAME, intr_idx,DAL_DEV_INTR_MAJOR_BASE+intr_idx);//�����ж��ַ������豸�ڵ�

    system(nod_name);

    poll_list.fd = open(dev_name, O_RDWR, 0644);
    if (poll_list.fd < 0)
    {
        return;
    }

    poll_list.events = POLLIN;


    while (1)
    {
        /* 1. poll */
        ret = sal_poll(&poll_list, 1, DAL_INTR_POLL_TIMEOUT);
        if (ret)
        {
            /* 2. if poll ok & have POLLIN, call dispatch */
            if (POLLIN == (poll_list.revents & POLLIN))
            {
                intr_handler->handler((void*)&(irq));
            }
        }
    }
}

int dal_interrupt_register(unsigned int irq, unsigned int prio,void (* isr)(void*), void* data)
{
    unsigned long arg = irq;
    char intr_name[32];
    unsigned int para = 0;
    dal_intr_para_t* p_para = (dal_intr_para_t*)&para;
    unsigned long intr_idx = 0;
    int ret = 0;
    dal_intr_info_t dal_info;

     printf("dal interrupt register\n");
    ret = dal_usrctrl_do_cmd(CMD_REG_INTERRUPTS, (unsigned long) &arg);

     if(ret<0)
     {
    	 return -1;
     }
    memset(intr_name, 0, 32);

    snprintf(intr_name, 32, "ctcIntr%d", irq);

    intr_idx = (unsigned long)data;
    if (DAL_MAX_INTR_NUM <= intr_idx)
    {
        return -1;
    }

    memset(&dal_info, 0, sizeof(dal_info));
    dal_info.irq = irq;
#if 1
    ret = dal_usrctrl_do_cmd(CMD_GET_INTR_INFO, (unsigned long)&dal_info);
    if (ret||dal_info.irq_idx>=DAL_MAX_INTR_NUM)
    {
        printf("IRQ %d not register in kernel!!!\n", irq);
        return -1;
    }
#endif
    intr_idx = dal_info.irq_idx;

    intr_handlers[intr_idx].handler = isr;
    intr_handlers[intr_idx].irq = irq;

    p_para->intr_idx = (int8)intr_idx;
    p_para->irq = (uint8)irq;

    sal_task_create(&intr_handlers[intr_idx].p_intr_thread,
                    intr_name,
						 SAL_STACK_SIZE,
                    prio,
                    dal_intr_thread,
                    (void*)((unsigned long)para));

    return ret;
}

int dal_interrupt_unregister(unsigned int irq)
{
    unsigned long arg = irq;
    int ret = 0;
    char nod_name[32];
    dal_intr_info_t dal_info;
    unsigned int irq_in_kernel = 0;

    sal_memset(&dal_info, 0, sizeof(dal_info));
    dal_info.irq = irq;
#if 1
    ret = dal_usrctrl_do_cmd(CMD_GET_INTR_INFO, (unsigned long)&dal_info);
    if (ret||dal_info.irq_idx>=DAL_MAX_INTR_NUM)
    {
        printf("IRQ %d not register in kernel!!!\n", irq);
        return -1;
    }
    irq_in_kernel = dal_info.irq_idx;
#endif
    ret = dal_usrctrl_do_cmd(CMD_UNREG_INTERRUPTS, (unsigned long) &arg);
    if (ret < 0)
    {
        return 0;
    }
    /* delete device node */
    sal_snprintf(nod_name, 32, "rm %s%d", DAL_INTR_DEV_NAME, irq_in_kernel);

    system(nod_name);

    /* destroy task */
    sal_task_destroy(intr_handlers[irq_in_kernel].p_intr_thread);
    sal_task_sleep(1);

    return ret;
}

int dal_interrupt_set_en(unsigned int irq, unsigned int enable)
{
    dal_intr_parm_t intr_para;
    int ret = 0;

    sal_memset(&intr_para, 0, sizeof(dal_intr_parm_t));
    intr_para.irq = irq;
    intr_para.enable = enable;
    ret = dal_usrctrl_do_cmd(CMD_EN_INTERRUPTS, (unsigned long) & intr_para);

    return ret;
}
